package xyz.sundy.sftp.client;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author Sundy
 * @since 2025/7/2 17:04
 */
@Slf4j
@Service
public class SftpConnectionPool {

    @Value("${sftp.config-dir:./config}")
    private String configDir;
    private final Map<String, GenericObjectPool<ChannelSftp>> sftpPoolMap = new ConcurrentHashMap<>();

    // 获取 SFTP 连接句柄
    public ChannelSftp getSftpHandler(String sftpName) throws SftpConnectionError {
        int maxRetries = 3; // 最大重试次数
        for (int i = 0; i < maxRetries; i++) {
            try {
                GenericObjectPool<ChannelSftp> sftpPool = getSftpPool(sftpName);
                ChannelSftp sftpHandler = sftpPool.borrowObject();
                if (isSftpConnectionValid(sftpHandler)) {
                    return sftpHandler;
                } else {
                    sftpPool.invalidateObject(sftpHandler);
                }
            } catch (Exception e) {
                log.error("第 {} 次尝试获取 SFTP 连接 {} 失败: {}", i + 1, sftpName, e.getMessage());
            }
        }
        throw new SftpConnectionError("获取 SFTP 连接 " + sftpName + " 失败，已达到最大重试次数");
    }

    // 检查 SFTP 连接是否有效
    private boolean isSftpConnectionValid(ChannelSftp sftpHandler) {
        try {
            if (sftpHandler != null && sftpHandler.isConnected()) {
                // 尝试执行简单操作验证连接
                sftpHandler.pwd();
                return true;
            }
        } catch (Exception e) {
            log.error("SFTP 连接验证失败: {}", e.getMessage());
        }
        return false;
    }

    // 将连接放回连接池
    public void returnSftpHandler(String sftpName, ChannelSftp sftpHandler) {
        GenericObjectPool<ChannelSftp> sftpPool = sftpPoolMap.get(sftpName);
        if (sftpPool != null) {
            if (isSftpConnectionValid(sftpHandler)) {
                sftpPool.returnObject(sftpHandler);
            } else {
                try {
                    sftpPool.invalidateObject(sftpHandler);
                } catch (Exception e) {
                    log.error("销毁无效 SFTP 连接失败: {}", e.getMessage());
                }
            }
        }
    }

    // 获取或创建 SFTP 连接池
    private GenericObjectPool<ChannelSftp> getSftpPool(String sftpName) throws SftpConnectionError {
        GenericObjectPool<ChannelSftp> sftpPool = sftpPoolMap.get(sftpName);
        if (sftpPool == null) {
            synchronized (this) {
                sftpPool = sftpPoolMap.get(sftpName);
                if (sftpPool == null) {
                    SftpPoolObjectFactory factory = new SftpPoolObjectFactory(sftpName, loadConfig(sftpName));
                    GenericObjectPoolConfig<ChannelSftp> config = new GenericObjectPoolConfig<>();
                    Map<String, String> sftpConfig = loadConfig(sftpName);
                    int poolSize = Integer.parseInt(sftpConfig.getOrDefault("sftp_pool_size", "1"));
                    config.setMaxTotal(poolSize);
                    config.setMinIdle(0);
                    config.setMaxWaitMillis(5000);
                    // 配置连接池定期检查空闲对象
                    config.setTestWhileIdle(true);
                    config.setTimeBetweenEvictionRunsMillis(60000); // 每分钟检查一次
                    config.setMinEvictableIdleTimeMillis(1800000); // 空闲 30 分钟后驱逐
                    sftpPool = new GenericObjectPool<>(factory, config);
                    sftpPoolMap.put(sftpName, sftpPool);
                }
            }
        }
        return sftpPool;
    }

    // 重置指定名称的 SFTP 连接池
    public void resetSftpPool(String sftpName) throws SftpConnectionError {
        GenericObjectPool<ChannelSftp> sftpPool = sftpPoolMap.get(sftpName);
        if (sftpPool != null) {
            try {
                sftpPool.close();
            } catch (Exception e) {
                log.error("关闭 SFTP 连接池 {} 失败: {}", sftpName, e.getMessage());
            }
            sftpPoolMap.remove(sftpName);
        }
    }

    // 加载 SFTP 配置文件
    private Map<String, String> loadConfig(String sftpName) throws SftpConnectionError {
        String configPath = configDir + "/sftp_config_" + sftpName + ".yml";
        Map<String, String> config = loadYaml(configPath);
        if (config != null) {
            return config;
        }
        log.warn("SFTP 配置文件不存在: {}", sftpName);
        throw new SftpConnectionError("[" + sftpName + "] SFTP 配置文件不存在");
    }

    // 加载 YAML 文件
    private Map<String, String> loadYaml(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            try (InputStream inputStream = new FileInputStream(file)) {
                Yaml yaml = new Yaml();
                return yaml.load(inputStream);
            } catch (FileNotFoundException e) {
                log.info("文件 {} 未找到，尽管路径存在，可能是权限问题", filePath);
            } catch (Exception e) {
                log.info("解析 YAML 文件 {} 失败，可能是文件格式错误: {}", filePath, e.getMessage());
            }
        }
        return null;
    }

    // 验证本地文件是否存在
    private void validateLocalFile(String localFile) throws FileNotFoundException {
        if (!new File(localFile).isFile()) {
            throw new FileNotFoundException("本地文件 [" + localFile + "] 不存在");
        }
    }

    // 确保远程目录存在
    private void ensureRemoteDirExists(ChannelSftp sftpHandler, String remoteDir) throws SftpException {
        try {
            sftpHandler.stat(remoteDir);
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                String[] remoteDirArray = remoteDir.split("/");
                String tmpDir = "";
                for (String tmp : remoteDirArray) {
                    if (tmp.isEmpty()) {
                        continue;
                    }
                    tmpDir = tmpDir.isEmpty() ? tmp : tmpDir + "/" + tmp;
                    try {
                        sftpHandler.stat(tmpDir);
                    } catch (SftpException e1) {
                        if (e1.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                            sftpHandler.mkdir(tmpDir);
                        }
                    }
                }
            } else {
                throw e;
            }
        }
    }

    // 确保本地目录存在
    private void ensureLocalDirExists(String localDir) {
        File dir = new File(localDir);
        if (!dir.isDirectory()) {
            dir.mkdirs();
        }
    }

    // 将本地文件上传到远程 SFTP 服务器
    public void putFile(String sftpName, String localFile, String remoteFile) throws SftpConnectionError, FileNotFoundException {
        validateLocalFile(localFile);
        String remoteDir = new File(remoteFile).getParent();
        ChannelSftp sftpHandler = getSftpHandler(sftpName);
        try {
            ensureRemoteDirExists(sftpHandler, remoteDir);
            sftpHandler.put(localFile, remoteFile);
        } catch (SftpException e) {
            log.info("上传文件 {} 到 {} 失败: {}", localFile, remoteFile, e.getMessage());
            throw new SftpConnectionError("上传文件 " + localFile + " 到 " + remoteFile + " 失败: " + e.getMessage());
        } finally {
            returnSftpHandler(sftpName, sftpHandler);
        }
    }

    // 从远程 SFTP 服务器下载文件到本地
    public void getFile(String sftpName, String localFile, String remoteFile) throws SftpConnectionError {
        ChannelSftp sftpHandler = getSftpHandler(sftpName);
        String localDir = new File(localFile).getParent();
        try {
            ensureLocalDirExists(localDir);
            sftpHandler.stat(remoteFile);
            sftpHandler.get(remoteFile, localFile);
        } catch (SftpException e) {
            log.info("从 {} 下载文件到 {} 失败: {}", remoteFile, localFile, e.getMessage());
            throw new SftpConnectionError("从 " + remoteFile + " 下载文件到 " + localFile + " 失败: " + e.getMessage());
        } finally {
            returnSftpHandler(sftpName, sftpHandler);
        }
    }
}